package org.streets.context.internal;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.slf4j.Logger;
import org.streets.commons.util.PathUtils;
/**
 * 
 * HiveContext properties && conf file auto load Symbol provider
 * 
 * @author dzb
 *
 */
public class ContextSymbolProvider implements org.apache.tapestry5.ioc.services.SymbolProvider {
	
	private Map<String, Long> _lastModifieds = new HashMap<String, Long>();
    private List<String> _configs = new ArrayList<String>();
	private Properties _properties = new Properties();
	private boolean _monitor = false;
    private org.slf4j.Logger LOG;
    
    public ContextSymbolProvider(Logger logger, List<ContextSymbol> props) {
        this.LOG = logger;
		for (ContextSymbol prop : props) {
			_properties.setProperty(prop.getName(), prop.getValue());
		}		
    }
	/**
	 * HiveMind service default initialize method
	 */
	public void _init_(String root) {		
		
		_properties.setProperty("context.root", root);
		
		File root_path = new File(PathUtils.toUnixPath(root));
		if (root_path.exists()) {			
			//loadModuleXml(root_path);
            configure(root_path, false, 0, 0);
			root_path = new File(root, "WEB-INF");
			if (root_path.exists()) {
				configure(root_path, true, 0, 1);
			}
		}
		
		for (String config : _configs) {			
			File file = new File(config);
			String name = config.toLowerCase();
			if (!file.exists() || !(name.endsWith("properties") && !name.endsWith("conf"))) {
				LOG.info("Ignore File ["+file.getAbsolutePath()+"]");
				continue;
			}
			
			Properties props = new Properties();	
			try {
				props.load(new FileInputStream(file));
			} catch (Exception e) {
				LOG.warn("Can not load properties file ["+config+"] :"+e.getMessage());
			}
			_properties.putAll(props);
			_lastModifieds.put(config, file.lastModified());
		}
		
		if (isMonitor()) {
			MonitorThread monitor = new MonitorThread();
			monitor.start();
		}
	}
	/**
	 *
	 * Load dir's config file,
	 * Include /dir/*.* and /dir/conf/*.*
	 * And /dir/conf/'s config file include sub directory
	 * 载入ClassPath中的xml和properties配置文件
	 * @param path
	 * @param sub indicats whether include sub directories
	 */
	private void configure(File path, boolean sub, int initDepth, final int depthLimit) {
		initDepth++;
		File[] files = path.listFiles();
		if (files == null) return ;
		for (File file : files) {
			//处理目录
			if (file.isDirectory()) {
				if (sub) {
					if (depthLimit<0 || initDepth<=depthLimit) {
						configure(file, sub, initDepth, depthLimit);
					}
				}
				//继续
				continue;
			} 
			//处理文件
			String ext = file.getName().toLowerCase();

			//载入properties文件
			if (ext.endsWith("properties") || ext.endsWith("conf")) {
				LOG.info("-> Loading property file : "+file.getAbsolutePath());
				try {
					String mp = PathUtils.toUnixPath(file.getAbsolutePath());
					_configs.add(mp.toLowerCase());
				} catch (Exception ex) {
					LOG.error("-> Load outer file config error : "+ex.getMessage());
				}
			}
		}
				
	}	
	
    /**
     * Get Context properties container for read or write
     * @return Properties 
     */
	public Properties properties() {
		return _properties;
	}

	/**
     * Get all properties which key start with prefix
	 */
	public Properties properties(String prefix) {
		Properties props = new Properties();
		Iterator<?> iter = _properties.keySet().iterator();
		while (iter.hasNext()) {
			String key = (String)iter.next();
			if (key.indexOf(prefix) >= 0) {
				props.put(key, _properties.getProperty(key));
			}
		}
		return props;
	}	
	/* (non-Javadoc)
	 * @see com.hivetech.streets.app.impl.SymbolStore#valueForSymbol(java.lang.String)
	 */
	public String valueForSymbol(String name) {
		return _properties.getProperty(name);
	}

	public boolean isMonitor() {
		return _monitor;
	}

	public void setMonitor(boolean monitor) {
		this._monitor = monitor;
	}    	

    public class MonitorThread extends Thread {
    	
    	
    	private boolean stop = false;
    	
    	public MonitorThread() {

    	}
    	
		public boolean isStop() {
			return stop;
		}

		public void setStop(boolean stop) {
			this.stop = stop;
		}

    	@Override
    	public void run() {
    		
    		while (!stop) {    			
				try {
					Thread.sleep(5000);					
				} catch (InterruptedException e) {
					LOG.warn("Thread is interrupted." +e.getMessage());
					return;
				}
				for (String path : _configs) {
					File f = new File(path);
					Long lf = _lastModifieds.get(path);
					if (lf == null) continue;
					if (f.lastModified() > lf) {
						LOG.info("-> File ["+path+"] has been modified, reloading...");
						Properties props = new Properties();
						try {
							_lastModifieds.put(path, f.lastModified());
							props.load(new FileInputStream(f));
							properties().putAll(props); //refresh
						} catch (Exception ex) {
							LOG.error("-> Reload ["+path+"] error: "+ex.getMessage());							
						}
					}
					try {
						Thread.sleep(300);					
					} catch (InterruptedException e) {
						LOG.warn("Thread is interrupted." +e.getMessage());
						return;
					}
				}
			}
		}
    }
}
